package com.giw.project.genre_managment;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.net.URL;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.lobobrowser.html.UserAgentContext;
import org.lobobrowser.html.parser.HtmlParser;
import org.lobobrowser.html.test.SimpleUserAgentContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.giw.project.exception_manager.UncheckedException;




public class RetrieveGenreMovies {

	private final static String genre_xpath="//h3[@class='property type' and ./text()='Genres: ']/" +
	"following-sibling::ul/li/a";
	private final static String N3pathfile="/Users/macbook/Desktop/IngegneriaInformatica" +
	"/GIW/Progetto_Finale/inputN3/linkedmdb-latest-dump";
	private Map<String,String> genre2uri;
	private final static String genre2uriPath="/Users/macbook/Desktop/" +
	"IngegneriaInformatica/GIW/Progetto_Finale/GenreManagment/genre2uri";
	private final static String predicate="http://data.linkedmdb.org/resource/movie/genre";
	private final static String movieDone="/Users/macbook/Desktop/" +
	"IngegneriaInformatica/GIW/Progetto_Finale/GenreManagment/movieDone";

	public RetrieveGenreMovies(){
		this.initializeMap();
	}

	private void initializeMap(){
		FileReader f;
		genre2uri=new HashMap<String,String>();
		try {
			f = new FileReader(genre2uriPath);
		} catch (FileNotFoundException e) {
			throw new UncheckedException("Path of uri2genre not found.\nMessage: "+e.getMessage());
		} 
		BufferedReader in =new BufferedReader(f);
		String line;
		try {
			line = in.readLine();
			while(line!=null){
				genre2uri.put(line.split("\t")[0].toLowerCase(), line.split("\t")[1]);
				line=in.readLine();
			}
			in.close();
			f.close();
		}catch(IOException e){
			throw new UncheckedException("Error while reading the file with movies" +
					"without genre.\nMessage: "+e.getMessage()); 
		}

	}

	private List<String> getMovieGenre(String link){
		List<String> genres=new LinkedList<String>();
		Logger.getLogger("org.lobobrowser").setLevel(Level.WARNING);
		UserAgentContext uacontext = new SimpleUserAgentContext();
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		Document document=null;
		try {
			DocumentBuilder builder = factory.newDocumentBuilder();
			URL url = new URL(link);
			InputStream in = url.openConnection().getInputStream();
			Reader reader = new InputStreamReader(in);
			document = builder.newDocument();            
			HtmlParser parser = new HtmlParser(uacontext, document);
			parser.parse(reader);
		}
		catch(ParserConfigurationException e){
			throw new UncheckedException(e.getMessage());
		}
		catch(IOException e){
			throw new UncheckedException(e.getMessage());
		}
		catch(SAXException e){
			throw new UncheckedException(e.getMessage());
		}
		if(document==null){
			throw new UncheckedException("Error trying to get the html page of the " +
			"article");
		}
		try {
			XPath xpath = XPathFactory.newInstance().newXPath();
			NodeList nodeList = (NodeList) xpath.evaluate(genre_xpath
					, document, XPathConstants.NODESET);
			if(nodeList==null){
				throw new UncheckedException("Error trying to evaluate the " +
				"x path expression");
			}
			int i=0;
			Element element=(Element) nodeList.item(i);
			while(element!=null){
				genres.add(element.getTextContent().toLowerCase());
				i++;
				element=(Element) nodeList.item(i);
			}
		}
		catch(XPathExpressionException e){
			throw new UncheckedException(e.getMessage());
		}

		if(genres.size()==0){
			System.out.println("There are no genres for this movie: "+link);
			return null;
		}

		return genres;
	}

	private void writeTriple(String subject,String predicate,String object){
		String line="<"+subject+">"+" "+"<"+predicate+">"+" "+"<"+object+">"+" .";

		FileOutputStream fos=null;
		try {
			fos = new FileOutputStream (N3pathfile, true);
		} catch (FileNotFoundException e) {
			throw new UncheckedException("Error while trying to open the N3 file to append new triples.\n" +
					"Error:"+e.getMessage());
		}  
		PrintWriter pw = new PrintWriter (fos);

		pw.println();
		pw.print(line);
		pw.close ();	
	}

	private void createTriplesMovieGenres(String movie,List<String> genres){
		for(String genre:genres){
			String uri_genre=this.genre2uri.get(genre);
			if(uri_genre!=null){
				this.writeTriple(movie, predicate, uri_genre);
				//this.writeTriple(uri_genre, predicate, movie);
			}
		}
	}

	public void addGenreToMovies() throws Exception{
		RetrieveOriginalMoviePages retrieve=new RetrieveOriginalMoviePages();
		Map<String,String> movieuri2link=retrieve.getOriginalPage();
		int count=0;
		for(String movie_uri:movieuri2link.keySet()){
			System.out.println("Movie processing: "+movie_uri);
			List<String> genres=this.getMovieGenre(movieuri2link.get(movie_uri));
			if(genres!=null){
				this.createTriplesMovieGenres(movie_uri, genres);
				count++;
				System.out.println(count);
			}
			this.addProcessedMovie(movie_uri);
		}
		System.out.println("Number of modified movies with new genre:"+count);

	}




	public static void main(String[] args) throws Exception{
		RetrieveGenreMovies r=new RetrieveGenreMovies();
		r.addGenreToMovies();
		Calendar c=new GregorianCalendar();
		System.out.println(c.getTime());
	}
	
	private void addProcessedMovie(String link) throws Exception{
		File f = new File(movieDone);
		FileOutputStream fos = null;
			fos = new FileOutputStream(f,true);
		PrintStream output = new PrintStream(fos);
		output.println("<"+link+">");
		output.close();
		fos.close();
	}

}
